home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / mgr / sparcmgr / demo2.zoo / demo / ex / ex_vmain.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-08-30  |  27.0 KB  |  1,304 lines

  1. /*
  2.  * Copyright (c) 1980 Regents of the University of California.
  3.  * All rights reserved.  The Berkeley software License Agreement
  4.  * specifies the terms and conditions for redistribution.
  5.  */
  6.  
  7. #ifndef lint
  8. static char *sccsid = "@(#)ex_vmain.c    7.8 (Berkeley) 3/9/87; 1.2 (Bellcore)    87/04/24";
  9. #endif not lint
  10.  
  11. #include "ex.h"
  12. #include "ex_tty.h"
  13. #include "ex_vis.h"
  14.  
  15. /*
  16.  * This is the main routine for visual.
  17.  * We here decode the count and possible named buffer specification
  18.  * preceding a command and interpret a few of the commands.
  19.  * Commands which involve a target (i.e. an operator) are decoded
  20.  * in the routine operate in ex_voperate.c.
  21.  */
  22.  
  23. #define    forbid(a)    { if (a) goto fonfon; }
  24.  
  25. vmain()
  26. {
  27.     register int c, cnt, i;
  28.     char esave[TUBECOLS];
  29.     char *oglobp;
  30.     short d;
  31.     line *addr;
  32.     int ind, nlput;
  33.     int shouldpo = 0;
  34.     int onumber, olist, (*OPline)(), (*OPutchar)();
  35.  
  36.     vch_mac = VC_NOTINMAC;
  37.  
  38.     /*
  39.      * If we started as a vi command (on the command line)
  40.      * then go process initial commands (recover, next or tag).
  41.      */
  42.     if (initev) {
  43.         oglobp = globp;
  44.         globp = initev;
  45.         hadcnt = cnt = 0;
  46.         i = tchng;
  47.         addr = dot;
  48.         goto doinit;
  49.     }
  50.  
  51.     /*
  52.      * NB:
  53.      *
  54.      * The current line is always in the line buffer linebuf,
  55.      * and the cursor at the position cursor.  You should do
  56.      * a vsave() before moving off the line to make sure the disk
  57.      * copy is updated if it has changed, and a getDOT() to get
  58.      * the line back if you mung linebuf.  The motion
  59.      * routines in ex_vwind.c handle most of this.
  60.      */
  61.     for (;;) {
  62.         /*
  63.          * Decode a visual command.
  64.          * First sync the temp file if there has been a reasonable
  65.          * amount of change.  Clear state for decoding of next
  66.          * command.
  67.          */
  68.         TSYNC();
  69.         vglobp = 0;
  70.         vreg = 0;
  71.         hold = 0;
  72.         seenprompt = 1;
  73.         wcursor = 0;
  74.         Xhadcnt = hadcnt = 0;
  75.         Xcnt = cnt = 1;
  76.         splitw = 0;
  77.         if (i = holdupd) {
  78.             if (state == VISUAL)
  79.                 ignore(peekkey());
  80.             holdupd = 0;
  81. /*
  82.             if (LINE(0) < ex_ZERO) {
  83.                 vclear();
  84.                 vcnt = 0;
  85.                 i = 3;
  86.             }
  87. */
  88.             if (state != VISUAL) {
  89.                 vcnt = 0;
  90.                 vsave();
  91.                 vrepaint(cursor);
  92.             } else if (i == 3)
  93.                 vredraw(WTOP);
  94.             else
  95.                 vsync(WTOP);
  96.             vfixcurs();
  97.         }
  98.  
  99.         /*
  100.          * Gobble up counts and named buffer specifications.
  101.          */
  102.         for (;;) {
  103. looptop:
  104. #ifdef MDEBUG
  105.             if (trace)
  106.                 fprintf(trace, "pc=%c",peekkey());
  107. #endif
  108.             if (isdigit(peekkey()) && peekkey() != '0') {
  109.                 hadcnt = 1;
  110.                 cnt = vgetcnt();
  111.                 forbid (cnt <= 0);
  112.             }
  113.             if (peekkey() != '"')
  114.                 break;
  115.             ignore(getkey()), c = getkey();
  116.             /*
  117.              * Buffer names be letters or digits.
  118.              * But not '0' as that is the source of
  119.              * an 'empty' named buffer spec in the routine
  120.              * kshift (see ex_temp.c).
  121.              */
  122.             forbid (c == '0' || !isalpha(c) && !isdigit(c));
  123.             vreg = c;
  124.         }
  125. reread:
  126.         /*
  127.          * Come to reread from below after some macro expansions.
  128.          * The call to map allows use of function key pads
  129.          * by performing a terminal dependent mapping of inputs.
  130.          */
  131. #ifdef MDEBUG
  132.         if (trace)
  133.             fprintf(trace,"pcb=%c,",peekkey());
  134. #endif
  135.         op = getkey();
  136.         maphopcnt = 0;
  137.         do {
  138.             /*
  139.              * Keep mapping the char as long as it changes.
  140.              * This allows for double mappings, e.g., q to #,
  141.              * #1 to something else.
  142.              */
  143.             c = op;
  144.             op = map(c,arrows);
  145. #ifdef MDEBUG
  146.             if (trace)
  147.                 fprintf(trace,"pca=%c,",c);
  148. #endif
  149.             /*
  150.              * Maybe the mapped to char is a count. If so, we have
  151.              * to go back to the "for" to interpret it. Likewise
  152.              * for a buffer name.
  153.              */
  154.             if ((isdigit(c) && c!='0') || c == '"') {
  155.                 ungetkey(c);
  156.                 goto looptop;
  157.             }
  158.             if (!value(REMAP)) {
  159.                 c = op;
  160.                 break;
  161.             }
  162.             if (++maphopcnt > 256)
  163.                 error("Infinite macro loop");
  164.         } while (c != op);
  165.  
  166.         /*
  167.          * Begin to build an image of this command for possible
  168.          * later repeat in the buffer workcmd.  It will be copied
  169.          * to lastcmd by the routine setLAST
  170.          * if/when completely specified.
  171.          */
  172.         lastcp = workcmd;
  173.         if (!vglobp)
  174.             *lastcp++ = c;
  175.  
  176.         /*
  177.          * First level command decode.
  178.          */
  179.         switch (c) {
  180.  
  181.         /*
  182.          * ^L        Clear screen e.g. after transmission error.
  183.          */
  184.  
  185.         /*
  186.          * ^R        Retype screen, getting rid of @ lines.
  187.          *        If in open, equivalent to ^L.
  188.          *        On terminals where the right arrow key sends
  189.          *        ^L we make ^R act like ^L, since there is no
  190.          *        way to get ^L.  These terminals (adm31, tvi)
  191.          *        are intelligent so ^R is useless.  Soroc
  192.          *        will probably foul this up, but nobody has
  193.          *        one of them.
  194.          */
  195.         case CTRL(a):
  196.             winch();
  197.             break;
  198.         case CTRL(l):
  199.         case CTRL(r):
  200.             if (c == CTRL(l) || (KR && *KR==CTRL(l))) {
  201.                 vclear();
  202.                 vdirty(0, vcnt);
  203.             }
  204.             if (state != VISUAL) {
  205.                 /*
  206.                  * Get a clean line, throw away the
  207.                  * memory of what is displayed now,
  208.                  * and move back onto the current line.
  209.                  */
  210.                 vclean();
  211.                 vcnt = 0;
  212.                 vmoveto(dot, cursor, 0);
  213.                 continue;
  214.             }
  215.             vredraw(WTOP);
  216.             /*
  217.              * Weird glitch -- when we enter visual
  218.              * in a very small window we may end up with
  219.              * no lines on the screen because the line
  220.              * at the top is too long.  This forces the screen
  221.              * to be expanded to make room for it (after
  222.              * we have printed @'s ick showing we goofed).
  223.              */
  224.             if (vcnt == 0)
  225.                 vrepaint(cursor);
  226.             vfixcurs();
  227.             continue;
  228.  
  229.         /*
  230.          * $        Escape just cancels the current command
  231.          *        with a little feedback.
  232.          */
  233.         case ESCAPE:
  234.             beep();
  235.             continue;
  236.  
  237.         /*
  238.          * @           Macros. Bring in the macro and put it
  239.          *        in vmacbuf, point vglobp there and punt.
  240.          */
  241.          case '@':
  242.             c = getesc();
  243.             if (c == 0)
  244.                 continue;
  245.             if (c == '@')
  246.                 c = lastmac;
  247.             if (isupper(c))
  248.                 c = tolower(c);
  249.             forbid(!islower(c));
  250.             lastmac = c;
  251.             vsave();
  252.             CATCH
  253.                 char tmpbuf[BUFSIZ];
  254.  
  255.                 regbuf(c,tmpbuf,sizeof(vmacbuf));
  256.                 macpush(tmpbuf, 1);
  257.             ONERR
  258.                 lastmac = 0;
  259.                 splitw = 0;
  260.                 getDOT();
  261.                 vrepaint(cursor);
  262.                 continue;
  263.             ENDCATCH
  264.             vmacp = vmacbuf;
  265.             goto reread;
  266.  
  267.         /*
  268.          * .        Repeat the last (modifying) open/visual command.
  269.          */
  270.         case '.':
  271.             /*
  272.              * Check that there was a last command, and
  273.              * take its count and named buffer unless they
  274.              * were given anew.  Special case if last command
  275.              * referenced a numeric named buffer -- increment
  276.              * the number and go to a named buffer again.
  277.              * This allows a sequence like "1pu.u.u...
  278.              * to successively look for stuff in the kill chain
  279.              * much as one does in EMACS with C-Y and M-Y.
  280.              */
  281.             forbid (lastcmd[0] == 0);
  282.             if (hadcnt)
  283.                 lastcnt = cnt;
  284.             if (vreg)
  285.                 lastreg = vreg;
  286.             else if (isdigit(lastreg) && lastreg < '9')
  287.                 lastreg++;
  288.             vreg = lastreg;
  289.             cnt = lastcnt;
  290.             hadcnt = lasthad;
  291.             vglobp = lastcmd;
  292.             goto reread;
  293.  
  294.         /*
  295.          * ^U        Scroll up.  A count sticks around for
  296.          *        future scrolls as the scroll amount.
  297.          *        Attempt to hold the indentation from the
  298.          *        top of the screen (in logical lines).
  299.          *
  300.          * BUG:        A ^U near the bottom of the screen
  301.          *        on a dumb terminal (which can't roll back)
  302.          *        causes the screen to be cleared and then
  303.          *        redrawn almost as it was.  In this case
  304.          *        one should simply move the cursor.
  305.          */
  306.         case CTRL(u):
  307.             if (hadcnt)
  308.                 ex_vSCROLL = cnt;
  309.             cnt = ex_vSCROLL;
  310.             if (state == VISUAL)
  311.                 ind = vcline, cnt += ind;
  312.             else
  313.                 ind = 0;
  314.             vmoving = 0;
  315.             vup(cnt, ind, 1);
  316.             vnline(NOSTR);
  317.             continue;
  318.  
  319.         /*
  320.          * ^D        Scroll down.  Like scroll up.
  321.          */
  322.         case CTRL(d):
  323. #ifdef TRACE
  324.         if (trace)
  325.             fprintf(trace, "before vdown in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
  326. #endif
  327.             if (hadcnt)
  328.                 ex_vSCROLL = cnt;
  329.             cnt = ex_vSCROLL;
  330.             if (state == VISUAL)
  331.                 ind = vcnt - vcline - 1, cnt += ind;
  332.             else
  333.                 ind = 0;
  334.             vmoving = 0;
  335.             vdown(cnt, ind, 1);
  336. #ifdef TRACE
  337.         if (trace)
  338.             fprintf(trace, "before vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
  339. #endif
  340.             vnline(NOSTR);
  341. #ifdef TRACE
  342.         if (trace)
  343.             fprintf(trace, "after vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
  344. #endif
  345.             continue;
  346.  
  347.         /*
  348.          * ^E        Glitch the screen down (one) line.
  349.          *        Cursor left on same line in file.
  350.          */
  351.         case CTRL(e):
  352.             if (state != VISUAL)
  353.                 continue;
  354.             if (!hadcnt)
  355.                 cnt = 1;
  356.             /* Bottom line of file already on screen */
  357.             forbid(lineDOL()-lineDOT() <= vcnt-1-vcline);
  358.             ind = vcnt - vcline - 1 + cnt;
  359.             vdown(ind, ind, 1);
  360.             vnline(cursor);
  361.             continue;
  362.  
  363.         /*
  364.          * ^Y        Like ^E but up
  365.          */
  366.         case CTRL(y):
  367.             if (state != VISUAL)
  368.                 continue;
  369.             if (!hadcnt)
  370.                 cnt = 1;
  371.             forbid(lineDOT()-1<=vcline); /* line 1 already there */
  372.             ind = vcline + cnt;
  373.             vup(ind, ind, 1);
  374.             vnline(cursor);
  375.             continue;
  376.  
  377.  
  378.         /*
  379.          * m        Mark position in mark register given
  380.          *        by following letter.  Return is
  381.          *        accomplished via ' or `; former
  382.          *        to beginning of line where mark
  383.          *        was set, latter to column where marked.
  384.          */
  385.         case 'm':
  386.             /*
  387.              * Getesc is generally used when a character
  388.              * is read as a latter part of a command
  389.              * to allow one to hit rubout/escape to cancel
  390.              * what you have typed so far.  These characters
  391.              * are mapped to 0 by the subroutine.
  392.              */
  393.             c = getesc();
  394.             if (c == 0)
  395.                 continue;
  396.  
  397.             /*
  398.              * Markreg checks that argument is a letter
  399.              * and also maps ' and ` to the end of the range
  400.              * to allow '' or `` to reference the previous
  401.              * context mark.
  402.              */
  403.             c = markreg(c);
  404.             forbid (c == 0);
  405.             vsave();
  406.             names[c - 'a'] = (*dot &~ 01);
  407.             ncols[c - 'a'] = cursor;
  408.             anymarks = 1;
  409.             continue;
  410.  
  411.         /*
  412.          * ^F        Window forwards, with 2 lines of continuity.
  413.          *        Count repeats.
  414.          */
  415.         case CTRL(f):
  416.             vsave();
  417.             if (vcnt > 2) {
  418.                 addr = dot + (vcnt - vcline) - 2 + (cnt-1)*basWLINES;
  419.                 forbid(addr > dol);
  420.                 dot = addr;
  421.                 vcnt = vcline = 0;
  422.             }
  423.             vzop(0, 0, '+');
  424.             continue;
  425.  
  426.         /*
  427.          * ^B        Window backwards, with 2 lines of continuity.
  428.          *        Inverse of ^F.
  429.          */
  430.         case CTRL(b):
  431.             vsave();
  432.             if (one + vcline != dot && vcnt > 2) {
  433.                 addr = dot - vcline + 2 - (cnt-1)*basWLINES;
  434.                 forbid (addr <= zero);
  435.                 dot = addr;
  436.                 vcnt = vcline = 0;
  437.             }
  438.             vzop(0, 0, '^');
  439.             continue;
  440.  
  441.         /*
  442.          * z        Screen adjustment, taking a following character:
  443.          *            z<CR>        current line to top
  444.          *            z<NL>        like z<CR>
  445.          *            z-        current line to bottom
  446.          *        also z+, z^ like ^F and ^B.
  447.          *        A preceding count is line to use rather
  448.          *        than current line.  A count between z and
  449.          *        specifier character changes the screen size
  450.          *        for the redraw.
  451.          *
  452.          */
  453.         case 'z':
  454.             if (state == VISUAL) {
  455.                 i = vgetcnt();
  456.                 if (i > 0)
  457.                     vsetsiz(i);
  458.                 c = getesc();
  459.                 if (c == 0)
  460.                     continue;
  461.             }
  462.             vsave();
  463.             vzop(hadcnt, cnt, c);
  464.             continue;
  465.  
  466.         /*
  467.          * Y        Yank lines, abbreviation for y_ or yy.
  468.          *        Yanked lines can be put later if no
  469.          *        changes intervene, or can be put in named
  470.          *        buffers and put anytime in this session.
  471.          */
  472.         case 'Y':
  473.             ungetkey('_');
  474.             c = 'y';
  475.             break;
  476.  
  477.         /*
  478.          * J        Join lines, 2 by default.  Count is number
  479.          *        of lines to join (no join operator sorry.)
  480.          */
  481.         case 'J':
  482.             forbid (dot == dol);
  483.             if (cnt == 1)
  484.                 cnt = 2;
  485.             if (cnt > (i = dol - dot + 1))
  486.                 cnt = i;
  487.             vsave();
  488.             vmacchng(1);
  489.             setLAST();
  490.             cursor = strend(linebuf);
  491.             vremote(cnt, join, 0);
  492.             notenam = "join";
  493.             vmoving = 0;
  494.             killU();
  495.             vreplace(vcline, cnt, 1);
  496.             if (!*cursor && cursor > linebuf)
  497.                 cursor--;
  498.             if (notecnt == 2)
  499.                 notecnt = 0;
  500.             vrepaint(cursor);
  501.             continue;
  502.  
  503.         /*
  504.          * S        Substitute text for whole lines, abbrev for c_.
  505.          *        Count is number of lines to change.
  506.          */
  507.         case 'S':
  508.             ungetkey('_');
  509.             c = 'c';
  510.             break;
  511.  
  512.         /*
  513.          * O        Create a new line above current and accept new
  514.          *        input text, to an escape, there.
  515.          *        A count specifies, for dumb terminals when
  516.          *        slowopen is not set, the number of physical
  517.          *        line space to open on the screen.
  518.          *
  519.          * o        Like O, but opens lines below.
  520.          */
  521.         case 'O':
  522.         case 'o':
  523.             vmacchng(1);
  524.             voOpen(c, cnt);
  525.             continue;
  526.  
  527.         /*
  528.          * C        Change text to end of line, short for c$.
  529.          */
  530.         case 'C':
  531.             if (*cursor) {
  532.                 ungetkey('$'), c = 'c';
  533.                 break;
  534.             }
  535.             goto appnd;
  536.  
  537.         /*
  538.          * ~    Switch case of letter under cursor
  539.          */
  540.         case '~':
  541.             {
  542.                 char mbuf[4];
  543.                 setLAST();
  544.                 mbuf[0] = 'r';
  545.                 mbuf[1] = *cursor;
  546.                 mbuf[2] = cursor[1]==0 ? 0 : ' ';
  547.                 mbuf[3] = 0;
  548.                 if (isalpha(mbuf[1]))
  549.                     mbuf[1] ^= ' ';    /* toggle the case */
  550.                 macpush(mbuf, 1);
  551.             }
  552.             continue;
  553.  
  554.  
  555.         /*
  556.          * A        Append at end of line, short for $a.
  557.          */
  558.         case 'A':
  559.             operate('$', 1);
  560. appnd:
  561.             c = 'a';
  562.             /* fall into ... */
  563.  
  564.         /*
  565.          * a        Appends text after cursor.  Text can continue
  566.          *        through arbitrary number of lines.
  567.          */
  568.         case 'a':
  569.             if (*cursor) {
  570.                 if (state == HARDOPEN)
  571.                     ex_putchar(*cursor);
  572.                 cursor++;
  573.             }
  574.             goto insrt;
  575.  
  576.         /*
  577.          * I        Insert at beginning of whitespace of line,
  578.          *        short for ^i.
  579.          */
  580.         case 'I':
  581.             operate('^', 1);
  582.             c = 'i';
  583.             /* fall into ... */
  584.  
  585.         /*
  586.          * R        Replace characters, one for one, by input
  587.          *        (logically), like repeated r commands.
  588.          *
  589.          * BUG:        This is like the typeover mode of many other
  590.          *        editors, and is only rarely useful.  Its
  591.          *        implementation is a hack in a low level
  592.          *        routine and it doesn't work very well, e.g.
  593.          *        you can't move around within a R, etc.
  594.          */
  595.         case 'R':
  596.             /* fall into... */
  597.  
  598.         /*
  599.          * i        Insert text to an escape in the buffer.
  600.          *        Text is arbitrary.  This command reminds of
  601.          *        the i command in bare teco.
  602.          */
  603.         case 'i':
  604. insrt:
  605.             /*
  606.              * Common code for all the insertion commands.
  607.              * Save for redo, position cursor, prepare for append
  608.              * at command and in visual undo.  Note that nothing
  609.              * is doomed, unless R when all is, and save the
  610.              * current line in a the undo temporary buffer.
  611.              */
  612.             vmacchng(1);
  613.             setLAST();
  614.             vcursat(cursor);
  615.             prepapp();
  616.             vnoapp();
  617.             doomed = c == 'R' ? 10000 : 0;
  618.             if(FIXUNDO)
  619.                 vundkind = VCHNG;
  620.             vmoving = 0;
  621.             CP(vutmp, linebuf);
  622.  
  623.             /*
  624.              * If this is a repeated command, then suppress
  625.              * fake insert mode on dumb terminals which looks
  626.              * ridiculous and wastes lots of time even at 9600B.
  627.              */
  628.             if (vglobp)
  629.                 hold = HOLDQIK;
  630.             vappend(c, cnt, 0);
  631.             continue;
  632.  
  633.         /*
  634.          * ^?        An attention, normally a ^?, just beeps.
  635.          *        If you are a vi command within ex, then
  636.          *        two ATTN's will drop you back to command mode.
  637.          */
  638.         case ATTN:
  639.             beep();
  640.             if (initev || peekkey() != ATTN)
  641.                 continue;
  642.             /* fall into... */
  643.  
  644.         /*
  645.          * ^\        A quit always gets command mode.
  646.          */
  647.         case QUIT:
  648.             /*
  649.              * Have to be careful if we were called
  650.              *    g/xxx/vi
  651.              * since a return will just start up again.
  652.              * So we simulate an interrupt.
  653.              */
  654.             if (inglobal)
  655.                 onintr();
  656.             /* fall into... */
  657.  
  658. #ifdef notdef
  659.         /*
  660.          * q        Quit back to command mode, unless called as
  661.          *        vi on command line in which case dont do it
  662.          */
  663.         case 'q':    /* quit */
  664.             if (initev) {
  665.                 vsave();
  666.                 CATCH
  667.                     error("Q gets ex command mode, :q leaves vi");
  668.                 ENDCATCH
  669.                 splitw = 0;
  670.                 getDOT();
  671.                 vrepaint(cursor);
  672.                 continue;
  673.             }
  674. #endif
  675.             /* fall into... */
  676.  
  677.         /*
  678.          * Q        Is like q, but always gets to command mode
  679.          *        even if command line invocation was as vi.
  680.          */
  681.         case 'Q':
  682.             vsave();
  683.             /*
  684.              * If we are in the middle of a macro, throw away
  685.              * the rest and fix up undo.
  686.              * This code copied from getbr().
  687.              */
  688.             if (vmacp) {
  689.                 vmacp = 0;
  690.                 if (inopen == -1)    /* don't screw up undo for esc esc */
  691.                     vundkind = VMANY;
  692.                 inopen = 1;    /* restore old setting now that macro done */
  693.             }
  694.             return;
  695.  
  696.  
  697.         /*
  698.          * ZZ        Like :x
  699.          */
  700.          case 'Z':
  701.             forbid(getkey() != 'Z');
  702.             oglobp = globp;
  703.             globp = "x";
  704.             vclrech(0);
  705.             goto gogo;
  706.             
  707.         /*
  708.          * P        Put back text before cursor or before current
  709.          *        line.  If text was whole lines goes back
  710.          *        as whole lines.  If part of a single line
  711.          *        or parts of whole lines splits up current
  712.          *        line to form many new lines.
  713.          *        May specify a named buffer, or the delete
  714.          *        saving buffers 1-9.
  715.          *
  716.          * p        Like P but after rather than before.
  717.          */
  718.         case 'P':
  719.         case 'p':
  720.             vmoving = 0;
  721. #ifdef notdef
  722.             forbid (!vreg && value(UNDOMACRO) && inopen < 0);
  723. #endif
  724.             /*
  725.              * If previous delete was partial line, use an
  726.              * append or insert to put it back so as to
  727.              * use insert mode on intelligent terminals.
  728.              */
  729.             if (!vreg && DEL[0]) {
  730.                 forbid ((DEL[0] & (QUOTE|TRIM)) == OVERBUF);
  731.                 vglobp = DEL;
  732.                 ungetkey(c == 'p' ? 'a' : 'i');
  733.                 goto reread;
  734.             }
  735.  
  736.             /*
  737.              * If a register wasn't specified, then make
  738.              * sure there is something to put back.
  739.              */
  740.             forbid (!vreg && unddol == dol);
  741.             /*
  742.              * If we just did a macro the whole buffer is in
  743.              * the undo save area.  We don't want to put THAT.
  744.              */
  745.             forbid (vundkind == VMANY && undkind==UNDALL);
  746.             vsave();
  747.             vmacchng(1);
  748.             setLAST();
  749.             i = 0;
  750.             if (vreg && partreg(vreg) || !vreg && pkill[0]) {
  751.                 /*
  752.                  * Restoring multiple lines which were partial
  753.                  * lines; will leave cursor in middle
  754.                  * of line after shoving restored text in to
  755.                  * split the current line.
  756.                  */
  757.                 i++;
  758.                 if (c == 'p' && *cursor)
  759.                     cursor++;
  760.             } else {
  761.                 /*
  762.                  * In whole line case, have to back up dot
  763.                  * for P; also want to clear cursor so
  764.                  * cursor will eventually be positioned
  765.                  * at the beginning of the first put line.
  766.                  */
  767.                 cursor = 0;
  768.                 if (c == 'P') {
  769.                     dot--, vcline--;
  770.                     c = 'p';
  771.                 }
  772.             }
  773.             killU();
  774.  
  775.             /*
  776.              * The call to putreg can potentially
  777.              * bomb since there may be nothing in a named buffer.
  778.              * We thus put a catch in here.  If we didn't and
  779.              * there was an error we would end up in command mode.
  780.              */
  781.             addr = dol;    /* old dol */
  782.             CATCH
  783.                 vremote(1, vreg ? putreg : put, vreg);
  784.             ONERR
  785.                 if (vreg == -1) {
  786.                     splitw = 0;
  787.                     if (op == 'P')
  788.                         dot++, vcline++;
  789.                     goto pfixup;
  790.                 }
  791.             ENDCATCH
  792.             splitw = 0;
  793.             nlput = dol - addr + 1;
  794.             if (!i) {
  795.                 /*
  796.                  * Increment undap1, undap2 to make up
  797.                  * for their incorrect initialization in the
  798.                  * routine vremote before calling put/putreg.
  799.                  */
  800.                 if (FIXUNDO)
  801.                     undap1++, undap2++;
  802.                 vcline++;
  803.                 nlput--;
  804.  
  805.                 /*
  806.                  * After a put want current line first line,
  807.                  * and dot was made the last line put in code
  808.                  * run so far.  This is why we increment vcline
  809.                  * above and decrease dot here.
  810.                  */
  811.                 dot -= nlput - 1;
  812.             }
  813. #ifdef TRACE
  814.             if (trace)
  815.                 fprintf(trace, "vreplace(%d, %d, %d), undap1=%d, undap2=%d, dot=%d\n", vcline, i, nlput, lineno(undap1), lineno(undap2), lineno(dot));
  816. #endif
  817.             vreplace(vcline, i, nlput);
  818.             if (state != VISUAL) {
  819.                 /*
  820.                  * Special case in open mode.
  821.                  * Force action on the screen when a single
  822.                  * line is put even if it is identical to
  823.                  * the current line, e.g. on YP; otherwise
  824.                  * you can't tell anything happened.
  825.                  */
  826.                 vjumpto(dot, cursor, '.');
  827.                 continue;
  828.             }
  829. pfixup:
  830.             vrepaint(cursor);
  831.             vfixcurs();
  832.             continue;
  833.  
  834.         /*
  835.          * ^^        Return to previous file.
  836.          *        Like a :e #, and thus can be used after a
  837.          *        "No Write" diagnostic.
  838.          */
  839.         case CTRL(^):
  840.             forbid (hadcnt);
  841.             vsave();
  842.             ckaw();
  843.             oglobp = globp;
  844.             if (value(AUTOWRITE))
  845.                 globp = "e! #";
  846.             else
  847.                 globp = "e #";
  848.             goto gogo;
  849.  
  850.         /*
  851.          * ^]        Takes word after cursor as tag, and then does
  852.          *        tag command.  Read ``go right to''.
  853.          */
  854.         case CTRL(]):
  855.             grabtag();
  856.             oglobp = globp;
  857.             globp = "tag";
  858.             goto gogo;
  859.  
  860.         /*
  861.          * &        Like :&
  862.          */
  863.          case '&':
  864.             oglobp = globp;
  865.             globp = "&";
  866.             goto gogo;
  867.             
  868.         /*
  869.          * ^G        Bring up a status line at the bottom of
  870.          *        the screen, like a :file command.
  871.          *
  872.          * BUG:        Was ^S but doesn't work in cbreak mode
  873.          */
  874.         case CTRL(g):
  875.             oglobp = globp;
  876.             globp = "file";
  877. gogo:
  878.             addr = dot;
  879.             vsave();
  880.             goto doinit;
  881.  
  882. #ifdef SIGTSTP
  883.         /*
  884.          * ^Z:    suspend editor session and temporarily return
  885.          *     to shell.  Only works with Berkeley/IIASA process
  886.          *    control in kernel.
  887.          */
  888.         case CTRL(z):
  889.             forbid(dosusp == 0 || !ldisc);
  890.             vsave();
  891.             oglobp = globp;
  892.             globp = "stop";
  893.             goto gogo;
  894. #endif
  895.  
  896.         /*
  897.          * :        Read a command from the echo area and
  898.          *        execute it in command mode.
  899.          */
  900.         case ':':
  901.             forbid (hadcnt);
  902.             vsave();
  903.             i = tchng;
  904.             addr = dot;
  905.             if (readecho(c)) {
  906.                 esave[0] = 0;
  907.                 goto fixup;
  908.             }
  909.             getDOT();
  910.             /*
  911.              * Use the visual undo buffer to store the global
  912.              * string for command mode, since it is idle right now.
  913.              */
  914.             oglobp = globp; strcpy(vutmp, genbuf+1); globp = vutmp;
  915. doinit:
  916.             esave[0] = 0;
  917.             fixech();
  918.  
  919.             /*
  920.              * Have to finagle around not to lose last
  921.              * character after this command (when run from ex
  922.              * command mode).  This is clumsy.
  923.              */
  924.             d = peekc; ungetchar(0);
  925.             if (shouldpo) {
  926.                 /*
  927.                  * So after a "Hit return..." ":", we do
  928.                  * another "Hit return..." the next time
  929.                  */
  930.                 pofix();
  931.                 shouldpo = 0;
  932.             }
  933.             CATCH
  934.                 /*
  935.                  * Save old values of options so we can
  936.                  * notice when they change; switch into
  937.                  * cooked mode so we are interruptible.
  938.                  */
  939.                 onumber = value(NUMBER);
  940.                 olist = value(LIST);
  941.                 OPline = Pline;
  942.                 OPutchar = Put_char;
  943. #ifndef CBREAK
  944.                 vcook();
  945. #endif
  946.                 commands(1, 1);
  947.                 if (dot == zero && dol > zero)
  948.                     dot = one;
  949. #ifndef CBREAK
  950.                 vraw();
  951. #endif
  952.             ONERR
  953. #ifndef CBREAK
  954.                 vraw();
  955. #endif
  956.                 copy(esave, vtube[WECHO], TUBECOLS);
  957.             ENDCATCH
  958.             fixol();
  959.             Pline = OPline;
  960.             Put_char = OPutchar;
  961.             ungetchar(d);
  962.             globp = oglobp;
  963.  
  964.             /*
  965.              * If we ended up with no lines in the buffer, make
  966.              * a line, and don't consider the buffer changed.
  967.              */
  968.             if (dot == zero) {
  969.                 fixzero();
  970.                 ex_sync();
  971.             }
  972.             splitw = 0;
  973.  
  974.             /*
  975.              * Special case: did list/number options change?
  976.              */
  977.             if (onumber != value(NUMBER))
  978.                 ignorf(setnumb(value(NUMBER)));
  979.             if (olist != value(LIST))
  980.                 ignorf(setlist(value(LIST)));
  981.  
  982. fixup:
  983.             /*
  984.              * If a change occurred, other than
  985.              * a write which clears changes, then
  986.              * we should allow an undo even if .
  987.              * didn't move.
  988.              *
  989.              * BUG: You can make this wrong by
  990.              * tricking around with multiple commands
  991.              * on one line of : escape, and including
  992.              * a write command there, but its not
  993.              * worth worrying about.
  994.              */
  995.             if (FIXUNDO && tchng && tchng != i)
  996.                 vundkind = VMANY, cursor = 0;
  997.  
  998.             /*
  999.              * If we are about to do another :, hold off
  1000.              * updating of screen.
  1001.              */
  1002.             if (vcnt < 0 && Peek_key == ':') {
  1003.                 getDOT();
  1004.                 shouldpo = 1;
  1005.                 continue;
  1006.             }
  1007.             shouldpo = 0;
  1008.  
  1009.             /*
  1010.              * In the case where the file being edited is
  1011.              * new; e.g. if the initial state hasn't been
  1012.              * saved yet, then do so now.
  1013.              */
  1014.             if (unddol == truedol) {
  1015.                 vundkind = VNONE;
  1016.                 Vlines = lineDOL();
  1017.                 if (!inglobal)
  1018.                     savevis();
  1019.                 addr = zero;
  1020.                 vcnt = 0;
  1021.                 if (esave[0] == 0)
  1022.                     copy(esave, vtube[WECHO], TUBECOLS);
  1023.             }
  1024.  
  1025.             /*
  1026.              * If the current line moved reset the cursor position.
  1027.              */
  1028.             if (dot != addr) {
  1029.                 vmoving = 0;
  1030.                 cursor = 0;
  1031.             }
  1032.  
  1033.             /*
  1034.              * If current line is not on screen or if we are
  1035.              * in open mode and . moved, then redraw.
  1036.              */
  1037.             i = vcline + (dot - addr);
  1038.             if (i < 0 || i >= vcnt && i >= -vcnt || state != VISUAL && dot != addr) {
  1039.                 if (state == CRTOPEN)
  1040.                     vup1();
  1041.                 if (vcnt > 0)
  1042.                     vcnt = 0;
  1043.                 vjumpto(dot, (char *) 0, '.');
  1044.             } else {
  1045.                 /*
  1046.                  * Current line IS on screen.
  1047.                  * If we did a [Hit return...] then
  1048.                  * restore vcnt and clear screen if in visual
  1049.                  */
  1050.                 vcline = i;
  1051.                 if (vcnt < 0) {
  1052.                     vcnt = -vcnt;
  1053.                     if (state == VISUAL)
  1054.                         vclear();
  1055.                     else if (state == CRTOPEN) {
  1056.                         vcnt = 0;
  1057.                     }
  1058.                 }
  1059.  
  1060.                 /*
  1061.                  * Limit max value of vcnt based on $
  1062.                  */
  1063.                 i = vcline + lineDOL() - lineDOT() + 1;
  1064.                 if (i < vcnt)
  1065.                     vcnt = i;
  1066.                 
  1067.                 /*
  1068.                  * Dirty and repaint.
  1069.                  */
  1070.                 vdirty(0, LINES);
  1071.                 vrepaint(cursor);
  1072.             }
  1073.  
  1074.             /*
  1075.              * If in visual, put back the echo area
  1076.              * if it was clobberred.
  1077.              */
  1078.             if (state == VISUAL) {
  1079.                 int sdc = destcol, sdl = destline;
  1080.  
  1081.                 splitw++;
  1082.                 vigoto(WECHO, 0);
  1083.                 for (i = 0; i < TUBECOLS - 1; i++) {
  1084.                     if (esave[i] == 0)
  1085.                         break;
  1086.                     vputchar(esave[i]);
  1087.                 }
  1088.                 splitw = 0;
  1089.                 vgoto(sdl, sdc);
  1090.             }
  1091.             continue;
  1092.  
  1093.         /*
  1094.          * u        undo the last changing command.
  1095.          */
  1096.         case 'u':
  1097.             vundo(1);
  1098.             continue;
  1099.  
  1100.         /*
  1101.          * U        restore current line to initial state.
  1102.          */
  1103.         case 'U':
  1104.             ex_vUndo();
  1105.             continue;
  1106.  
  1107. fonfon:
  1108.             beep();
  1109.             vmacp = 0;
  1110.             inopen = 1;    /* might have been -1 */
  1111.             continue;
  1112.         }
  1113.  
  1114.         /*
  1115.          * Rest of commands are decoded by the operate
  1116.          * routine.
  1117.          */
  1118.         operate(c, cnt);
  1119.     }
  1120. }
  1121.  
  1122. /*
  1123.  * Grab the word after the cursor so we can look for it as a tag.
  1124.  */
  1125. grabtag()
  1126. {
  1127.     register char *cp, *dp;
  1128.  
  1129.     cp = vpastwh(cursor);
  1130.     if (*cp) {
  1131.         dp = lasttag;
  1132.         do {
  1133.             if (dp < &lasttag[sizeof lasttag - 2])
  1134.                 *dp++ = *cp;
  1135.             cp++;
  1136.         } while (isalpha(*cp) || isdigit(*cp) || *cp == '_'
  1137. #ifdef LISPCODE
  1138.             || (value(LISP) && *cp == '-')
  1139. #endif LISPCODE
  1140.             );
  1141.         *dp++ = 0;
  1142.     }
  1143. }
  1144.  
  1145. /*
  1146.  * Before appending lines, set up addr1 and
  1147.  * the command mode undo information.
  1148.  */
  1149. prepapp()
  1150. {
  1151.  
  1152.     addr1 = dot;
  1153.     deletenone();
  1154.     addr1++;
  1155.     appendnone();
  1156. }
  1157.  
  1158. /*
  1159.  * Execute function f with the address bounds addr1
  1160.  * and addr2 surrounding cnt lines starting at dot.
  1161.  */
  1162. vremote(cnt, f, arg)
  1163.     int cnt, (*f)(), arg;
  1164. {
  1165.     register int oing = inglobal;
  1166.  
  1167.     addr1 = dot;
  1168.     addr2 = dot + cnt - 1;
  1169.     inglobal = 0;
  1170.     if (FIXUNDO)
  1171.         undap1 = undap2 = dot;
  1172.     (*f)(arg);
  1173.     inglobal = oing;
  1174.     if (FIXUNDO)
  1175.         vundkind = VMANY;
  1176.     vmcurs = 0;
  1177. }
  1178.  
  1179. /*
  1180.  * Save the current contents of linebuf, if it has changed.
  1181.  */
  1182. vsave()
  1183. {
  1184.     char temp[LBSIZE];
  1185.  
  1186.     CP(temp, linebuf);
  1187.     if (FIXUNDO && vundkind == VCHNG || vundkind == VCAPU) {
  1188.         /*
  1189.          * If the undo state is saved in the temporary buffer
  1190.          * vutmp, then we sync this into the temp file so that
  1191.          * we will be able to undo even after we have moved off
  1192.          * the line.  It would be possible to associate a line
  1193.          * with vutmp but we assume that vutmp is only associated
  1194.          * with line dot (e.g. in case ':') above, so beware.
  1195.          */
  1196.         prepapp();
  1197.         strcLIN(vutmp);
  1198.         putmark(dot);
  1199.         vremote(1, yank, 0);
  1200.         vundkind = VMCHNG;
  1201.         notecnt = 0;
  1202.         undkind = UNDCHANGE;
  1203.     }
  1204.     /*
  1205.      * Get the line out of the temp file and do nothing if it hasn't
  1206.      * changed.  This may seem like a loss, but the line will
  1207.      * almost always be in a read buffer so this may well avoid disk i/o.
  1208.      */
  1209.     getDOT();
  1210.     if (strcmp(linebuf, temp) == 0)
  1211.         return;
  1212.     strcLIN(temp);
  1213.     putmark(dot);
  1214. }
  1215.  
  1216. #undef    forbid
  1217. #define    forbid(a)    if (a) { beep(); return; }
  1218.  
  1219. /*
  1220.  * Do a z operation.
  1221.  * Code here is rather long, and very uninteresting.
  1222.  */
  1223. vzop(hadcnt, cnt, c)
  1224.     bool hadcnt;
  1225.     int cnt;
  1226.     register int c;
  1227. {
  1228.     register line *addr;
  1229.  
  1230.     if (state != VISUAL) {
  1231.         /*
  1232.          * Z from open; always like a z=.
  1233.          * This code is a mess and should be cleaned up.
  1234.          */
  1235.         vmoveitup(1, 1);
  1236.         vgoto(outline, 0);
  1237.         ostop(normf);
  1238.         setoutt();
  1239.         addr2 = dot;
  1240.         vclear();
  1241.         destline = WECHO;
  1242.         zop2(Xhadcnt ? Xcnt : value(WINDOW) - 1, '=');
  1243.         if (state == CRTOPEN)
  1244.             putnl();
  1245.         putNFL();
  1246.         termreset();
  1247.         Outchar = vputchar;
  1248.         ignore(ostart());
  1249.         vcnt = 0;
  1250.         outline = destline = 0;
  1251.         vjumpto(dot, cursor, 0);
  1252.         return;
  1253.     }
  1254.     if (hadcnt) {
  1255.         addr = zero + cnt;
  1256.         if (addr < one)
  1257.             addr = one;
  1258.         if (addr > dol)
  1259.             addr = dol;
  1260.         markit(addr);
  1261.     } else
  1262.         switch (c) {
  1263.  
  1264.         case '+':
  1265.             addr = dot + vcnt - vcline;
  1266.             break;
  1267.  
  1268.         case '^':
  1269.             addr = dot - vcline - 1;
  1270.             forbid (addr < one);
  1271.             c = '-';
  1272.             break;
  1273.  
  1274.         default:
  1275.             addr = dot;
  1276.             break;
  1277.         }
  1278.     switch (c) {
  1279.  
  1280.     case '.':
  1281.     case '-':
  1282.         break;
  1283.  
  1284.     case '^':
  1285.         forbid (addr <= one);
  1286.         break;
  1287.  
  1288.     case '+':
  1289.         forbid (addr >= dol);
  1290.         /* fall into ... */
  1291.  
  1292.     case CR:
  1293.     case NL:
  1294.         c = CR;
  1295.         break;
  1296.  
  1297.     default:
  1298.         beep();
  1299.         return;
  1300.     }
  1301.     vmoving = 0;
  1302.     vjumpto(addr, NOSTR, c);
  1303. }
  1304.